iT邦幫忙

2023 iThome 鐵人賽

DAY 7
0

嗨大家好,我是Eric ~ 在前端開發中,我們經常會遇到需要去重(去除重複元素)的場景,特別是當我們處理復雜的數據結構,例如物件陣列時。
接下來就來分享如何使用 JavaScript 的 Set 和一個自定義的函數來達到去重的目的,假設我們有一個物件陣列,如下:

const arr = [
    { a: 1, b: 2 },
    { a: 1, b: 2 },
    { a: 1, b: 2, c: { a: 1, b: 2 } },
    { a: 2, b: 1, c: { a: 2, b: 1 } }
];

使用 Set 來去重

物件陣列去重, 只要物件的key值相同,就視為相同物件

如果使用 set 集合來去重,需要使用 === 來判斷是否重復(NaN認為是相同的、0 and -0 則不同),所以在這裡使用 Set,即便判斷的物件內容相同,但是因為記憶體位置不同(物件的指向不同),所以不會被視為相同物件

const newArr = [ ...new Set(arr)];
console.log(newArr);

// 輸出:
[
    { a: 1, b: 2 },
    { a: 1, b: 2 },
    { a: 1, b: 2, c: { a: 1, b: 2 } },
    { a: 2, b: 1, c: { a: 2, b: 1 } }
]
// 沒有任何去重

所以在這裡我們嘗試用原始迴圈、**JSON.stringify()**的方式來去重:

// 1. 使用淺拷貝來創建一個新的陣列(陣列中的物件依然會引用原陣列中對應物件的記憶體位置,代表不是同一個物件)
const newArr = [ ...arr ];

// 2. 外部迴圈用於遍歷新陣列中的每一個元素
for (let i = 0; i < newArr.length; i++) {
    
    // 3. 內部迴圈用於與外部迴圈當前元素進行比較,以找出重複的物件
    for (let j = i + 1; j < newArr.length; j++) {
        
        // 4. 使用 JSON.stringify 將當前比較的兩個物件轉成字串
        // 這樣做是為了能夠簡單地比較兩個物件是否具有相同的屬性和值
        if (JSON.stringify(newArr[i]) === JSON.stringify(newArr[j])) {
            
            // 5. 如果找到重複的物件,則從新陣列中刪除它
            newArr.splice(j, 1);
            
            // 6. 由於刪除了一個元素,陣列長度減少了,為了不讓 j 越界,將 j 減 1
            j--;
        }
    }
}

// 7. 輸出去重後的新陣列。
console.log(newArr);

你可以發現 1、 2個物件順序一樣,內容也一樣,但是第3、4個物件雖然內容一樣,但是順序不一樣。

使用 JSON.stringify() 進行物件比較有一個限制,那就是它對於屬性順序需要一樣

// 輸出:
[
    { a: 1, b: 2 }, 
    // { a: 1, b: 2 }, // 重複的部分被刪除
		
	// 順序不一樣,所以沒辦法去重
	{ a: 1, b: 2, c: { a: 1, b: 2 } },
    { b: 2, a: 1, c: { b: 2, a: 1 } }
]

使用自定義函數來去重

所以我們可以選擇 使用一個比較函式來處理這個問題

// 主函數:判斷兩個值(val1 和 val2)是否相等
function equals(val1, val2) {
    // 1. 檢查 val1 和 val2 是否為物件
    // 如果其中一個不是物件,則使用 Object.is 進行比較
    // Object.is 可以正確處理 NaN 和 -0 的情況
    if (!isObject(val1) || !isObject(val2)) {
        return Object.is(val1, val2);
    }
    
    // 2. 判斷 val1 和 val2 是否引用同一個記憶體位置
    // 如果是,則它們一定是相等的
    if (val1 === val2) return true;
    
    // 3. 獲取 val1 和 val2 的所有 Key(屬性名)
    const val1Keys = Object.keys(val1);
    const val2Keys = Object.keys(val2);
    
    // 4. 比較兩個物件的 Key 的數量,如果數量不同,則兩個物件不可能相等
    if (val1Keys.length !== val2Keys.length) return false;
    
    // 5. 逐一比較兩個物件的每一個 Key
    for (const key of val1Keys) {
        // 5.1 如果 val2 沒有 val1 的某個 Key,則兩者不相等
        if (!val2Keys.includes(key)) return false;
        
        // 5.2 遞迴地比較 val1 和 val2 的這一個 Key 對應的值
        // 如果它們對應的值不相等,則整個 val1 和 val2 也不相等
        if (!equals(val1[key], val2[key])) return false;
    }
    
    // 6. 經過上面所有的檢查,如果函數還沒有返回 false,則可以確定 val1 和 val2 是相等的
    return true;
}

// 輔助函數:檢查一個值是否是物件
// 注意:在 JavaScript 中,null 也被視為一種物件,所以需要額外判斷
function isObject(value) {
    return value !== null && typeof value === 'object';
}

成功去重

console.log(newArr);
// 輸出:[ { a: 1, b: 2 }, { a: 1, b: 2, c: { a: 1, b: 2 } } ]

使用場景

物件陣列去重在許多場景下都是非常有用的,比如:

  • 數據可視化:去除重複的數據點
  • 購物車:確保同一個商品不會被重複添加
  • 表單驗證:去除重複的選項或標籤

結語

我們探討了如何使用 Set 和自定義函數來進行物件陣列的去重操作,雖然 Set 是一個很好的工具,但它在處理物件時有其局限性。

所以,瞭解如何自定義函數來進行更精確的比較判斷是非常重要的,才能在不同的場景下有更多的選擇,使用最佳的方案,那麼這次的分享到這邊~謝謝大家~~~明天見!


上一篇
Day6 - 進度監聽器 XHR
下一篇
Day8 - 移動端頁面寬高自適應
系列文
JavaScript 是什麼?可以吃嗎?20
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言